#include "CoffChanger.h"
#include "Pattern.h"
#include "time.h"
#include "Config.h"
#include "Common.h"


CCoffChanger * CCoffChanger::GetInstance( CPattern * pattern )
{
	return new CCoffChangerGA( pattern );
}

CCoffChanger::CCoffChanger( CPattern * pattern )
{
	m_pPattern = pattern;
	srand( (int) time(0) );
}

CCoffChangerGA::CCoffChangerGA( CPattern * pattern ) : CCoffChanger( pattern )
{
	m_pvecCoffList = new vector<CCoffItem*>();
}

CCoffChangerGA::~CCoffChangerGA()
{
	ClearList();
}

bool CCoffChangerGA::Change( char _coff[REF_PIXEL_NUMBER] )
{

	if ( m_pvecCoffList->size() == 0 || ++m_iterCurrentCoff == m_pvecCoffList->end() )
	{
		// the first time to run or it's time to beign a new round
		do
		{
			DoSelection();
			DoCrossOver();
			DoMutation();
			// DoCleanUp();
		}
		while( m_pvecCoffList->size() == 0 );

		m_iterCurrentCoff = m_pvecCoffList->begin();
	}

	memcpy( _coff, (*m_iterCurrentCoff)->coff, sizeof( char ) * REF_PIXEL_NUMBER );
	return true;
}

bool CCoffChangerGA::DoSelection()
{
	ClearList();

	vector<CCoffItem*> * coff_list = m_pPattern->m_pCoffBucket->m_pvecCoffList;
	CCoffItem * item;

	if ( coff_list->size() < GA_INIT_GROUP_SIZE )
	{
		// use all coffs in the list and padding to GA_INIT_GROUP_SIZE
		while ( m_pvecCoffList->size() < GA_INIT_GROUP_SIZE )
		{
			int length;
			if ( GA_INIT_GROUP_SIZE-m_pvecCoffList->size() > coff_list->size() )
			{
				length = coff_list->size();
			}
			else
			{
				length = GA_INIT_GROUP_SIZE-m_pvecCoffList->size();
			}

			for( int i=0; i<length; i++ )
			{
				item = new CCoffItem( (*coff_list)[i]->coff, 0 );	
				m_pvecCoffList->push_back( item );
			}
		}
	}
	else
	{
		// pick up the top GA_INIT_GROUP_SIZE
		for( int i=0; i<GA_INIT_GROUP_SIZE; i++ )
		{
			item = new CCoffItem( (*coff_list)[i]->coff, 0 );	
			m_pvecCoffList->push_back( item );
		}
	}

	return true;
}

bool CCoffChangerGA::DoCrossOver()
{
	CCoffItem * item;
	for( int i=0; i<GA_INIT_GROUP_SIZE; i+=2 )
	{
		item = new CCoffItem( (*m_pvecCoffList)[i]->coff, 0 );
		m_pvecCoffList->push_back( item );
		item = new CCoffItem( (*m_pvecCoffList)[i+1]->coff, 0 );
		m_pvecCoffList->push_back( item );

		DoCrossOverWithDNA( (*m_pvecCoffList)[i+GA_INIT_GROUP_SIZE]->coff, (*m_pvecCoffList)[i+GA_INIT_GROUP_SIZE+1]->coff );
	}

	return true;
}

bool CCoffChangerGA::DoMutation()
{
	int pos;
	int dna_pos;
	int max_pos = sizeof(char) * REF_PIXEL_NUMBER * 8;
	unsigned int pattern_mask = CConfig::GetConfig()->pattern_mask;

	for( int i=0; i<GA_MUTATION_TIMES; i++ )
	{
		dna_pos = GetRand( GA_SELECTION_SIZE );

		do
		{
			pos = GetRand( max_pos );
		}
		while( GetPatternBit( pattern_mask, pos/8 ) == 0 );

		// TODO:
		(*m_pvecCoffList)[dna_pos]->coff[pos/8] ^= 1 << (7 - pos % 8);
	}

	return true;
}

int CCoffChanger::GetRand( int n )
{
	return int(n*rand()/(RAND_MAX+1.0));
}

bool CCoffChangerGA::ClearList()
{
	vector<CCoffItem*>::iterator iter = m_pvecCoffList->begin();
	for ( ; iter!=m_pvecCoffList->end(); iter++ )
	{
		delete *iter;
	}

	m_pvecCoffList->clear();

	return true;
}

bool CCoffChangerGA::DoCrossOverWithDNA( char * first, char * second )
{
	// TODO bitwise

	// r ranges from 1 to max-1
	int r = GetRand( REF_PIXEL_NUMBER * sizeof(char) - 1 ) + 1;
	
	char temp[REF_PIXEL_NUMBER];
	memcpy( temp, first, r * sizeof(char) );
	memcpy( first, second, r * sizeof(char) );
	memcpy( second, temp, r * sizeof(char) );

	return true;
}

bool CCoffChangerGA::DoCleanUp()
{
	for( vector<CCoffItem*>::iterator i=m_pvecCoffList->begin(); i!=m_pvecCoffList->end(); )
	{
		if( m_pPattern->m_pCoffBucket->FindDuplication( (*i)->coff ) )
		{
			vector<CCoffItem*>::iterator j=i;
			m_pvecCoffList->erase( j );
		}
		else
		{
			i++;
		}
	}

	return true;
}